/************************************************************************
This file is part of lpc2xxx package for the simpleRTJ virtual machine.

This file is covered by the GNU GPL with the following exception:
  As a special exception, the copyright holders of this library give you permission
  to link this library with independent modules to produce an executable, regardless
  of the license terms of these independent modules, and to copy and distribute the
  resulting executable under terms of your choice, provided that you also meet, for
  each linked independent module, the terms and conditions of the license of that
  module. An independent module is a module which is not derived from or based on
  this library. If you modify this library, you may extend this exception to your
  version of the library, but you are not obligated to do so. If you do not wish
  to do so, delete this exception statement from your version.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

Copyright (c) 2000-2006 RTJ Computing Pty. Ltd. All rights reserved.
***********************************************************************/
package lpc2xxx.comm;


/**
 * Class providing access to I2C (two wire bus) on the LPC21xx.<br>
 * Add your own code to implement additional features if required.
 */
public class I2C
{
    /** Default frequency (100kHz) will be used when trying to use un-initialized I2C */
    private final static int defFreq = 100000;
    /** Set to true when I2C has been initialized */
    private static boolean initialized = false;
    /** Holds the I2C device address */
    private int addr;

    /** OK response code */
    public final static int OK      = 1;

    /**
     * I2C constructor - creates a new instance of this object.<br>
     * If the I2C module hasn't been initialized yet the constructor will call 
     * <code>init</code> method and sets the default operating frequency (100kHz).
     * @param addr address of the I2C device
     */
    public I2C(int addr)
    {
        this.addr = addr;
        if (!initialized)
            init(defFreq);
    }

    /**
     * Enables and initializes the I2C with the provided frequency.<br>
     * Call <code>init</code> method prior creating an instance of I2C class.
     * @param freq frequency (in Hz) at which the I2C will operate (max. 400kHz)
     */
    public static void init(int freq)
    {
        init0(freq);
        initialized = true;
    }

    /**
     * Sends data in the supplied byte array on the I2C bus. The start condition 
     * is automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data)
    {
        return write(addr, 0, 0, data, 0, data.length);
    }

    /**
     * Sends data in the supplied byte array (or its portion) on the I2C bus. The start condition 
     * is automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @param offs offset to the first byte in <code>data</code> array
     * @param len number of bytes to transmit
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data, int offs, int len)
    {
        return write(addr, 0, 0, data, offs, len);
    }

    /**
     * Sends data in the supplied byte array on the I2C bus. One extra command byte is sent after 
     * the address byte. The start condition is automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @param extra extra command byte to send after the address byte.
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data, byte extra)
    {
        return write(addr, 1, extra, data, 0, data.length);
    }

    /**
     * Sends data in the supplied byte array (or its portion) on the I2C bus. One extra 
     * command byte is sent after the address byte. The start condition is 
     * automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @param extra extra command byte to send after the address byte.
     * @param offs offset to the first byte in <code>data</code> array
     * @param len number of bytes to transmit
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data, byte extra, int offs, int len)
    {
        return write(addr, 1, extra, data, offs, len);
    }

    /**
     * Sends data in the supplied byte array on the I2C bus. One extra 
     * command word (two bytes) is sent after the address byte. The start condition 
     * is automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @param extra extra command word (two bytes) to send after the address byte.
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data, short extra)
    {
        return write(addr, 2, extra, data, 0, data.length);
    }

    /**
     * Sends data in the supplied byte array (or its portion) on the I2C bus. One extra 
     * command word (two bytes) is sent after the address byte. The start condition is 
     * automatically generated.<br>
     * <b>Note:</b> No stop condition is generated after the transmission - this must
     * be done after calling this method.
     * @param data byte array containing data
     * @param extra extra command word (two bytes) to send after the address byte.
     * @param offs offset to the first byte in <code>data</code> array
     * @param len number of bytes to transmit
     * @return true if successful, otherwise false is returned
     */
    public boolean write(byte[] data, short extra, int offs, int len)
    {
        return write(addr, 2, extra, data, offs, len);
    }

    /**
     * Read a specified number of bytes from the I2C bus.<br>
     * <b>Note:</b> No start condition is generated. This must be done prior
     * calling this method.
     * @param dest byte array that will receive data
     * @param offs offset to <code>dest</code> array
     * @param len number of bytes to receive
     * @return OK if successful or status byte on error
     */
    public int read(byte[] dest, int offs, int len)
    {
        return read(addr + 1, dest, offs, len);
    }

    /**
     * Waits till slave device returns ACK (after busy period).
     * @return true if successful, otherwise false is returned
     */
    public boolean poll()
    {
        return poll(addr);
    }

    /** 
     * Native method that performs the actual I2C initialization.
     * @param freq frequency at which the I2C will operate.
     */
    private static native void init0(int freq);

    /**
     * Generates a start condition on I2C when bus is free.
     * @return OK if successful or status byte on error
     */
    public native int start();

    /**
     * Generates a repeat start condition on I2C when bus is free.
     * @return OK if successful or status byte on error
     */
    public native int reStart();

    /**
     * Generates a stop condition in master mode or recovers from an error
     * condition in slave mode.
     */
    public native void stop();

    /**
     * Returns the current I2C status by reading the I2STAT register.
     * @return current value of I2STAT register.
     */
    public native int status();

    /**
     * Waits till slave device returns ACK (after busy period).
     * @param addr device address
     * @return true if successful, otherwise false is returned
     */
    public native boolean poll(int addr);

    /**
     * Sends data on the I2C bus. 
     * @param addr device address
     * @param extra number of extra bytes sent after the address byte, possible values are 0, 1 or 2.
     * @param extraCmd extra command bytes to transmit based on <code>extra</code> parameter:<br>
     *        <code>extra</code> = 0 - this parameter is ignored<br>
     *        <code>extra</code> = 1 - one extra byte is sent (lower 8-bits)<br>
     *        <code>extra</code> = 2 - two extra bytes are sent (lower 16-bits)
     * @param data byte array containing data
     * @param offs offset to the first byte in <code>data</code> array
     * @param len number of bytes to transmit
     * @return true if successful
     */
    private native boolean write(int addr, int extra, int extraCmd, byte[] data, int offs, int len);

    /**
     * Reads data from I2C bus. 
     * @param addr device address
     * @param dest byte array that will receive data
     * @param offs offset to <code>dest</code> array
     * @param len number of bytes to receive
     * @return OK if successful or status byte on error
     */
    private native int read(int addr, byte[] dest, int offs, int len);
}
